package io.vov.vitamio;

import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;
import io.vov.vitamio.MediaPlayer.OnCompletionListener;
import io.vov.vitamio.MediaPlayer.OnErrorListener;
import io.vov.vitamio.MediaPlayer.OnHWRenderFailedListener;
import io.vov.vitamio.MediaPlayer.OnInfoListener;
import io.vov.vitamio.MediaPlayer.OnPreparedListener;
import io.vov.vitamio.MediaPlayer.OnSeekCompleteListener;
import io.vov.vitamio.MediaPlayer.OnTimedTextListener;
import io.vov.vitamio.MediaPlayer.OnVideoSizeChangedListener;
import io.vov.vitamio.utils.StringUtils;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;

import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Binder;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.util.Log;
import android.view.SurfaceHolder;

public class VideoPlayerService extends Service implements
		OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener,
		OnVideoSizeChangedListener, OnErrorListener, OnInfoListener,
		OnSeekCompleteListener, OnTimedTextListener, IMediaControl {
	private MediaPlayer mPlayer;
	private VPlayerListener mListener;
	private VPlayerVideoListener mVideoListener;

	private Uri mUri;
	private Uri mOldUri;
	private long mSeekTo = -1l;
	private boolean mFromNotification;
	private String[] mSubPaths;
	private boolean mInitialized;
	private final IBinder mBinder = new VideoPlayerBinder();
	private TelephonyManager mTelephonyManager;
	private int mCurrentState;
	private SurfaceHolder mSurfaceHolder;
	public static final int VPLYAER_NOTIFICATION_ID = 1;

	public static final int STATE_PREPARED = -1;
	public static final int STATE_PLAYING = 0;
	public static final int STATE_NEED_RESUME = 1;
	public static final int STATE_STOPPED = 2;
	public static final int STATE_RINGING = 3;

	private int mLastAudioTrack = -1;
	private String mLastSubTrack;
	private int mLastSubTrackId = -1;
	private long mMediaId = -1l;

	public class VideoPlayerBinder extends Binder {
		public VideoPlayerService getService() {
			return VideoPlayerService.this;
		}
	}

	@Override
	public void onCreate() {
		super.onCreate();

		boolean result = Vitamio.initialize(getApplicationContext());

		logd("��Ƶ������������ʼ����Ƶ����� = %b", result);

		mInitialized = false;
		mTelephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
		mTelephonyManager.listen(mPhoneListener,
				PhoneStateListener.LISTEN_CALL_STATE);

	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {

		logd("onStartCommand");

		if (Vitamio.isInitialized(this)) {
			vplayerInit(intent.getBooleanExtra("isHWCodec", false));
		} else {
			stopSelf();
		}

		logd("VidePlayService CREATE OK");

		return super.onStartCommand(intent, flags, startId);
	}

	private void vplayerInit(boolean isHWCodec) {

		logd("��ʼ�����������Ƿ�ʹ��Ӳ������ = %b", isHWCodec);

		mPlayer = new MediaPlayer(
				VideoPlayerService.this.getApplicationContext(), isHWCodec);
		mPlayer.setOnHWRenderFailedListener(new OnHWRenderFailedListener() {
			@Override
			public void onFailed() {
				if (mListener != null)
					mListener.onHWRenderFailed();
			}
		});
		mPlayer.setOnBufferingUpdateListener(VideoPlayerService.this);
		mPlayer.setOnCompletionListener(VideoPlayerService.this);
		mPlayer.setOnPreparedListener(VideoPlayerService.this);
		mPlayer.setOnVideoSizeChangedListener(VideoPlayerService.this);
		mPlayer.setOnErrorListener(VideoPlayerService.this);
		mPlayer.setOnInfoListener(VideoPlayerService.this);

	}

	public void releaseContext() {
		if (mPlayer != null)
			mPlayer.release();
		mPlayer = null;
	}

	@Override
	public IBinder onBind(Intent intent) {
		logd("onBind : " + intent.getPackage());

		if (mPlayer == null) {
			if (Vitamio.isInitialized(this)) {
				vplayerInit(intent.getBooleanExtra("isHWCodec", false));
			}
		}
		return mBinder;
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		release(true);
		releaseContext();
	}

	public boolean isInitialized() {
		return mInitialized;
	}

	private String mTitle;

	public boolean initialize(Uri filePath, String displayName, long startPos,
			VPlayerListener listener, boolean isHWCodec) {
		if (mPlayer == null)
			vplayerInit(isHWCodec);

		setTitle(displayName);
		mListener = listener;
		mOldUri = mUri;
		mUri = filePath;
		mSeekTo = startPos;
		mMediaId = -1;
		mLastAudioTrack = -1;
		mLastSubTrackId = -1;
		mLastSubTrack = "";
		setMediaTrack();
		logd("%s ==> %s, %s, %s, %s", mOldUri, mUri, mInitialized, mPrepared,
				mVideoSizeKnown);
		mFromNotification = mInitialized && mUri != null
				&& mUri.equals(mOldUri);
		if (mListener != null)
			mListener.onOpenStart();
		if (!mFromNotification)
			openVideo();
		else
			openSuccess();
		return mInitialized;
	}

	public void setVideoURI(Uri uri) {
		mUri = uri;
		initialize(uri, "", 0, null, false);
	}

	public void setVideoPath(String url) {
		setVideoURI(Uri.parse(url));

	}

	private void setMediaTrack() {
	}

	public void setTitle(String title) {
		mTitle = title;
	}

	public String getTitle() {
		return mTitle;
	}

	private void openVideo() {
		if (mUri == null || mPlayer == null)
			return;

		mPlayer.reset();
		mInitialized = false;
		mPrepared = false;
		mVideoSizeKnown = false;

		try {
			mPlayer.setScreenOnWhilePlaying(true);
			mPlayer.setDataSource(VideoPlayerService.this, mUri);
			if (mSurfaceHolder != null && mSurfaceHolder.getSurface() != null
					&& mSurfaceHolder.getSurface().isValid())
				mPlayer.setDisplay(mSurfaceHolder);
			mPlayer.prepareAsync();
		} catch (IllegalArgumentException e) {
			loge("openVideo", e);
		} catch (IllegalStateException e) {
			loge("openVideo", e);
		} catch (IOException e) {
			loge("openVideo", e);
		}
	}

	public Uri getUri() {
		return mUri;
	}

	public long getMediaId() {
		return mMediaId;
	}

	public int getLastAudioTrack() {
		return mLastAudioTrack;
	}

	public String getLastSubTrack() {
		return mLastSubTrack;
	}

	public int getLastSubTrackId() {
		return mLastSubTrackId;
	}

	public void setVPlayerListener(VPlayerListener listener) {
		mListener = listener;
	}

	public void setState(int state) {
		mCurrentState = state;
	}

	public boolean needResume() {
		return mInitialized
				&& (mCurrentState == STATE_NEED_RESUME || mCurrentState == STATE_PREPARED);
	}

	public boolean ringingState() {
		return mInitialized && mCurrentState == STATE_RINGING;
	}

	@Override
	public void release() {
		release(true);
	}

	private void release(boolean all) {

		if (mPlayer != null) {
			if (mListener != null)
				mListener.onCloseStart();
			mPlayer.reset();
			mInitialized = false;
			mPrepared = false;
			mVideoSizeKnown = false;
			if (mListener != null)
				mListener.onCloseComplete();

			logd("�ͷ���Ƶ...%b", all);
		}
		if (all) {
			mListener = null;
			mUri = null;
		}
	}

	@Override
	public void stop() {
		if (mInitialized && mPlayer.isPlaying()) {
			logd("��ͣ��Ƶ...");
			mPlayer.pause();
		}
	}

	@Override
	public void start() {
		if (mInitialized && mPlayer.isPlaying() == false) {
			logd("������Ƶ...%d,%d", mPlayer.getCurrentPosition(),
					mPlayer.getCurrentPosition());

			// ����Ѿ�������ϣ������¿�ʼ
			if (mPlayer.getCurrentPosition() + 1 >= mPlayer.getDuration()) {
				seekTo(0);
				logd("������Ƶ...�Ѿ�������ϣ����¿�ʼ��");
			}

			mPlayer.start();
			setState(STATE_PLAYING);
		}
	}

	private Handler mHandler = new Handler(new Handler.Callback() {

		@Override
		public boolean handleMessage(Message msg) {

			// ������Ƶ��ʾ
			if (msg.what == 10086 && mPlayer != null) {
				mPlayer.setDisplay(mSurfaceHolder);
			}

			return false;
		}
	});

	/**
	 * ������Ƶ��ʾ��
	 * 
	 * @param holder
	 */
	public void setDisplay(SurfaceHolder holder) {
		try {
			mSurfaceHolder = holder;
			if (mPlayer != null && holder != null) {
				logd("setDisplay...");
				stop();
				releaseSurface();

				// �����л�̫�죬Ҫ�ȴ�һ��
				Thread thread = new Thread(new Runnable() {

					@Override
					public void run() {
						try {
							Thread.sleep(500);
						} catch (InterruptedException e) {
							e.printStackTrace();
						} finally {
							Message.obtain(mHandler, 10086).sendToTarget();
						}
					}
				});

				thread.start();

				// mPlayer.setDisplay(holder);

			}
		} catch (Exception e) {
			if (mListener != null)
				mListener.onOpenFailed(0, 0);
		}
	}

	public void releaseSurface() {
		if (mInitialized)
			mPlayer.releaseDisplay();
	}

	@Override
	public boolean isPlaying() {
		return (mInitialized && mPlayer.isPlaying());
	}

	public int getVideoWidth() {
		if (mInitialized)
			return mPlayer.getVideoWidth();
		return 0;
	}

	public int getVideoHeight() {
		if (mInitialized)
			return mPlayer.getVideoHeight();
		return 0;
	}

	public float getVideoAspectRatio() {
		if (mInitialized)
			return mPlayer.getVideoAspectRatio();
		return 0f;
	}

	@Override
	public long getDuration() {
		if (mInitialized)
			return mPlayer.getDuration();
		return 0;
	}

	@Override
	public long getCurrentPosition() {
		if (mInitialized)
			return mPlayer.getCurrentPosition();
		return 0;
	}

	public Bitmap getCurrentFrame() {
		if (mInitialized)
			return mPlayer.getCurrentFrame();
		return null;
	}

	public float getBufferProgress() {
		if (mInitialized)
			return mPlayer.getBufferProgress();
		return 0f;
	}

	@Override
	public void seekTo(long position) {
		if (mInitialized){
			logd("��ת���ȣ�%d", position);
			mPlayer.seekTo(position);
		}
	}

	public String getMetaEncoding() {
		if (mInitialized)
			return mPlayer.getMetaEncoding();
		return null;
	}

	public void setAudioTrack(int num) {
		if (mInitialized)
			mPlayer.selectTrack(num);
	}

	public int getAudioTrack() {
		if (mInitialized)
			return mPlayer.getAudioTrack();
		return 0;
	}

	public void setSubShown(boolean shown) {
		if (mInitialized)
			mPlayer.setTimedTextShown(shown);
	}

	protected boolean isBuffering() {
		return (mInitialized && mPlayer.isBuffering());
	}

	protected void setBuffer(int bufSize) {
		if (mInitialized)
			mPlayer.setBufferSize(bufSize);
	}

	protected void setVolume(float left, float right) {
		if (mInitialized) {
			if (left <= 0f)
				left = 0f;
			else if (left >= 1f)
				left = 1f;
			if (right <= 0f)
				right = 0f;
			else if (right >= 1f)
				right = 1f;
			mPlayer.setVolume(left, right);
		}
	}

	protected void setVideoQuality(int quality) {
		if (mInitialized)
			mPlayer.setVideoQuality(quality);
	}

	protected void setDeinterlace(boolean deinterlace) {
		if (mInitialized)
			mPlayer.setDeinterlace(deinterlace);
	}

	// public int getSubLocation() {
	// if (mInitialized)
	// return mPlayer.getSubLocation();
	// return -1;
	// }
	//
	protected void setSubEncoding(String encoding) {
		if (mInitialized) {
			String enc = encoding.equals(VP.DEFAULT_SUB_ENCODING) ? null
					: encoding;
			mPlayer.setTimedTextEncoding(enc);// setSubEncoding ->
												// setTimedTextEncoding
		}
	}

	public void setSubPath(String subPath) {
		if (mInitialized)
			mPlayer.addTimedTextSource(subPath);
	}

	private PhoneStateListener mPhoneListener = new PhoneStateListener() {
		@Override
		public void onCallStateChanged(int state, String incomingNumber) {
			switch (state) {
			case TelephonyManager.CALL_STATE_IDLE:
				break;
			case TelephonyManager.CALL_STATE_OFFHOOK:
			case TelephonyManager.CALL_STATE_RINGING:
				if (isPlaying()) {
					stop();
					setState(STATE_RINGING);
				}
				break;
			default:
				break;
			}
		}
	};

	private boolean mVideoSizeKnown = false;
	private boolean mPrepared = false;

	@Override
	public void onVideoSizeChanged(MediaPlayer mp, int width, int height) {
		mVideoSizeKnown = true;
		float a = mp.getVideoAspectRatio();

		logd("onVideoSizeChanged(%d,%d) - %f ", width, height, a);

		if (mVideoListener != null)
			mVideoListener.onVideoSizeChanged(width, height, a);
	}

	@Override
	public void onPrepared(MediaPlayer arg0) {
		logd("onPrepared");
		if (mListener != null) {
			mListener.onPrepared(arg0);
			mListener.onMedialControl(this);
		}

		mPrepared = true;
		openSuccess();
	}

	private void openSuccess() {
		logd("openSuccess");

		mInitialized = true;
		if (!mFromNotification && mSeekTo > 0 && mSeekTo < 1)
			seekTo(mSeekTo);
		mSeekTo = -1;
		if (mListener != null) {
			mListener.onOpenSuccess();
			mListener.onMedialControl(this);
		}
		if (!mFromNotification) {
			setSubEncoding(VP.DEFAULT_SUB_ENCODING);
			if (mUri != null)
				mSubPaths = getSubFiles(mUri.getPath());
			if (mSubPaths != null)
				setSubPath(getCanonical(new File(mSubPaths[0])));
			setSubShown(VP.DEFAULT_SUB_SHOWN);
		}
	}

	public String getCanonical(File f) {
		if (f == null)
			return null;

		try {
			return f.getCanonicalPath();
		} catch (IOException e) {
			return f.getAbsolutePath();
		}
	}

	@Override
	public void onCompletion(MediaPlayer arg0) {
		logd("onCompletion");

		if (mListener != null) {
			mListener.onPlaybackComplete();
		} else {
			release(true);
		}
	}

	@Override
	public void onBufferingUpdate(MediaPlayer mp, int p) {
		logd("onBufferingUpdate��%d", p);

		if (mListener != null)
			mListener.onBufferingUpdate(mp, p);
	}

	@Override
	public void onSeekComplete(MediaPlayer mp) {
		logd("onSeekComplete");

		if (mListener != null)
			mListener.onSeekComplete(mp);
	}

	@Override
	public boolean onInfo(MediaPlayer mp, int arg1, int arg2) { 
//		Log.e("position---duration--->", mp.getCurrentPosition()+"---"+mp.getDuration()+"---"+arg1+"---"+MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING);

		switch (arg1) {
		case MediaPlayer.MEDIA_INFO_VIDEO_TRACK_LAGGING: // ������
			if (mListener != null) {
				long position = mp.getCurrentPosition();
				long duration = mp.getDuration();
				mListener.onPlaying(position, duration,
						StringUtils.generateTime(position),
						StringUtils.generateTime(duration));
			}
			break;
		case MediaPlayer.MEDIA_INFO_BUFFERING_START:
			stop();
			if (mListener != null)
				mListener.onBufferStart();
			else
				stop();
			break;
		case MediaPlayer.MEDIA_INFO_BUFFERING_END:

			if (mListener != null)
				mListener.onBufferComplete();
			else
				start();
			break;
		case MediaPlayer.MEDIA_INFO_DOWNLOAD_RATE_CHANGED:
			if (mListener != null){
				mListener.onDownloadRateChanged(arg2);
				long position = mp.getCurrentPosition();
				long duration = mp.getDuration();
				mListener.onPlaying(position, duration,
						StringUtils.generateTime(position),
						StringUtils.generateTime(duration));
			}
			break;
		}
		return true;
	}

	@Override
	public boolean onError(MediaPlayer arg0, int arg1, int arg2) {
		logd("onError��(%d,%d)", arg1, arg2);
		mListener.onOpenFailed(arg1, arg2);
		return true;
	}

	private String[] getSubFiles(String videoPath) {
		ArrayList<String> files = new ArrayList<String>();
		for (String ext : MediaPlayer.SUB_TYPES) {
			File s = new File(videoPath.substring(0,
					videoPath.lastIndexOf('.') > 0 ? videoPath.lastIndexOf('.')
							: videoPath.length())
					+ ext);
			if (s.exists() && s.isFile() && s.canRead())
				files.add(s.getAbsolutePath());
		}

		if (files.isEmpty())
			return null;
		else
			return files.toArray(new String[files.size()]);
	}

	@Override
	public void onTimedText(String text) {
		logd("onTimedText��%s", text);
		if (mListener != null)
			mListener.onSubChanged(text);
	}

	@Override
	public void onTimedTextUpdate(byte[] pixels, int width, int height) {
		logd("onTimedTextUpdate��(%d,%d)", width, height);
		if (mListener != null)
			mListener.onSubChanged(pixels, width, height);
	}

	public void setVPlayerVideoListener(VPlayerVideoListener l) {
		this.mVideoListener = l;
	}

	private void logd(String msg, Object... args) {
		Log.d("VideoPlayerService", String.format(msg, args));
	}

	private void loge(String msg, Object... args) {
		Log.e("VideoPlayerService", String.format(msg, args));
	}

	@Override
	public boolean isPlayCompleted() {
		if (mInitialized == false || mPlayer == null)
			return true;

		if (mPlayer.isPlaying()) {
			return false;

		}
		return mPlayer.getCurrentPosition() + 1 >= mPlayer.getDuration();
	}

	// @Override
	// public void surfaceDestroyed(SurfaceHolder holder) {
	// logd("surfaceDestroyed");
	// if (mPlayer != null && isInitialized()) {
	// if (isPlaying()) {
	// stop();
	// setState(STATE_NEED_RESUME);
	// }
	// releaseSurface();
	// if (needResume())
	// start();
	// }
	// }
	//
	// @Override
	// public void surfaceCreated(SurfaceHolder holder) {
	// logd("surfaceCreated");
	// if (mPlayer != null)
	// mPlayer.setDisplay(holder);
	// }
	//
	// @Override
	// public void surfaceChanged(SurfaceHolder holder, int format, int width,
	// int height) {
	// logd("surfaceChanged");
	// if (mListener != null)
	// mListener.onVideoSizeChanged(width, height, 0, 0, 0);
	// }

}
